﻿

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Ports;
using System.Timers;
using System.Threading;
using System.Runtime.InteropServices;
using System.Collections.Concurrent;
using System.Configuration;
using System.IO;



namespace MCComDLL
{

    public delegate void dataRecv(COMMessage recvMsg);


    public class McCOMChannel
    {
        private static ISocket socket;

        private System.Threading.Timer sendTimer;

        private static bool sendWakeUPDate = false, sendCurDate = false;

        private static DateTime wakeUpDate;

        public delegate void newDataRecv(S_DATA data, S_IO_DATA io);

      
        newDataRecv callBack;

        public newDataRecv CallBack
        {
            get { return callBack; }
            set { callBack = value; }
        }

        public McCOMChannel()
        {
           
        }

        public bool open()
        {
            try
            {
                socket = new TCPIP_Socket();
                socket.open(this.recvMsgHandler);
                AppSettingsReader config = new AppSettingsReader();
                int period = (int)config.GetValue("sendperiod", typeof(int));
                sendTimer = new System.Threading.Timer(new TimerCallback(sendData), null, 100, period);
                sendCurDate = true;
            }
            catch (Exception any)
            {
                return false;
            }
            return true;

        }
        
        public void close()
        {
            sendTimer.Dispose();
            socket.close();
        }

        public void recvMsgHandler(COMMessage rx)
        {
            switch (rx.id)
            {
                case MC_ID_SEND_DATA:
                    readData(rx);
                    break;
            }

            S_IO_DATA io;
            io.ENAPC_0 = getIO(0);
            io.ENAPC_1 = getIO(1);
            io.PC_RUNNING = getIO(2);
            io.S0 = getIO(3);
            io.S1 = getIO(4);
            io.R0 = getRelay(0);
            io.R1 = getRelay(1);
            io.R2 = getRelay(2);
            io.R3 = getRelay(3);
            io.R4 = getRelay(4);
            io.R5 = getRelay(5);
            io.R6 = getRelay(6);
            io.R7 = getRelay(7);

            if (callBack != null)
                callBack(s_data, io);
        }

         /* IDs sent from MC to PC */
        const byte MC_ID_SEND_DATA			= 1;

        /* IDs sent from PC to MC */
        const byte PC_ID_REQ_DATA			= 1;
        const byte PC_ID_SEND_DATE          = 2;
        const byte PC_ID_SEND_WUDATE        = 3;
        const byte PC_ID_SEND_DATA          = 4;


        /// <summary>
        /// singleton der Klasse
        /// </summary>
        private static readonly McCOMChannel instance = new McCOMChannel();

        public static McCOMChannel Instance
        {
            get
            {
                return instance;
            }
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S_DATA
        {
            public byte fanpwm_ch0;

            public byte ledpwm_ch0;
            public byte ledpwm_ch1;
            public byte ledpwm_ch2;
            public byte ledpwm_ch3;

            public Int16 dimm_ch0;
            public Int16 dimm_ch1;
            public Int16 dimm_ch2;

            public byte io;
            public byte relay;

            public byte tempair_t0;
            public byte tempair_t1;
            public byte tempair_t2;
            public byte tempair_t3;
            public byte tempair_t4;
            public byte tempair_t5;
            public byte tempair_t6;
            public byte tempair_t7;

            public UInt16 tempwater_t0;
            public UInt16 tempwater_t1;

            public UInt16 fanrpm_v0;
            public UInt16 fanrpm_v1;
            public UInt16 fanrpm_v2;
            public UInt16 fanrpm_v3;

        };

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct S_SEND_DATA
        {
            public byte fanpwm_ch0;

            public byte ledpwm_ch0;
            public byte ledpwm_ch1;
            public byte ledpwm_ch2;
            public byte ledpwm_ch3;

            public byte dimm_ch0;
            public byte dimm_ch1;
            public byte dimm_ch2;

            public byte io;
            public byte relay;

        };

        private static S_DATA s_data;
        private static S_SEND_DATA s_senddata;

        private DateTime mc_date, mc_wudate;

        private enum E_STATE
        {
            sof1,
            sync,
            id,
            dlc,
            eof,
            unknown
        }


        public struct S_IO_DATA
        {
            public bool ENAPC_0;
            public bool ENAPC_1;
            public bool PC_RUNNING;
            public bool S0;
            public bool S1;
            public bool R0;
            public bool R1;
            public bool R2;
            public bool R3;
            public bool R4;
            public bool R5;
            public bool R6;
            public bool R7;
        }


        
        static Object lock_port = new Object();

        public void setFanPWM(int ch, int value)
        {
                switch (ch)
                {
                    case 0:
                        s_senddata.fanpwm_ch0 = (byte)(2.55 * value);
                        break;
                };                    
        }

        public int getFanPWN(int ch)
        {
            switch (ch)
            {
                case 0:
                    return s_data.fanpwm_ch0;
                case 1:
                    return s_data.fanpwm_ch0;
                case 2:
                    return s_data.fanpwm_ch0;
                default:
                    return 0;
            };         
        }

        public int getFanRPM(int ch)
        {
            switch (ch)
            {
                case 0:
                    return s_data.fanrpm_v0;
                case 1:
                    return s_data.fanrpm_v1;
                case 2:
                    return s_data.fanrpm_v2;
                case 3:
                    return s_data.fanrpm_v3;
                case 4:
                    return s_data.fanrpm_v3;
                case 5:
                    return s_data.fanrpm_v3;
                case 6:
                    return s_data.fanrpm_v3;
                case 7:
                    return s_data.fanrpm_v3;
                case 8:
                    return s_data.fanrpm_v3;
                case 9:
                    return s_data.fanrpm_v3;
                case 10:
                    return s_data.fanrpm_v3;
                case 11:
                    return s_data.fanrpm_v3;                
                default:
                    return 0;

            };
        }

        public int getWaterFlow(int ch)
        {
            switch (ch)
            {
                case 0:
                    return 0;
                case 1:
                    return 0;
                case 2:
                    return 0;
                case 3:
                    return 0;
                default:
                    return 0;
            };
        }

        public int getLEDPWM(int ch)
        {
            switch (ch)
            {
                case 0:
                    return s_data.ledpwm_ch0;
                case 1:
                    return s_data.ledpwm_ch1;
                case 2:
                    return s_data.ledpwm_ch2;
                default:
                    return 0;
            };
        }

        public void setLEDPWM(int ch, int value)
        {

            switch (ch)
            {
                case 0:
                    s_senddata.ledpwm_ch0 = (byte)(2.55 + value);
                    break;
                case 1:
                    s_senddata.ledpwm_ch1 = (byte)(2.55 + value);
                    break;
                case 2:
                    s_senddata.ledpwm_ch2 = (byte)(2.55 + value);
                    break;

            };


        }

        public int get230VDIM(int ch)
        {

            switch (ch)
            {
                case 0:
                    return s_data.dimm_ch0;
                case 1:
                    return s_data.dimm_ch1;
                default:
                    return 0;

            };
       
        }

        public void set230VDIM(int ch, int value)
        {
            switch (ch)
            {
                case 3:
                    s_senddata.dimm_ch0 = (byte)value;
                    break;
                case 4:
                    s_senddata.dimm_ch1 = (byte)value;
                    break;

            };
        }

        public double getAirTemp(int tempSensor)
        {
            switch (tempSensor)
            {
                case 0:
                    return s_data.tempair_t0 / 2.0;
                case 1:
                    return s_data.tempair_t1 / 2.0;
                case 2:
                    return s_data.tempair_t2 / 2.0;
                case 3:
                    return s_data.tempair_t3 / 2.0;
                case 4:
                    return s_data.tempair_t4 / 2.0;
                case 5:
                    return s_data.tempair_t5 / 2.0;
                case 6:
                    return s_data.tempair_t6 / 2.0;
                case 7:
                    return s_data.tempair_t7 / 2.0;
                default:
                    return 0.0;
            }
            
        }

        public double getWaterTemp(int tempSensor)
        {
            switch (tempSensor)
            {
                case 0:
                    return s_data.tempwater_t0;
                case 1:
                    return s_data.tempwater_t1;
                case 2:
                    return 0;
                case 3:
                    return 0;
                default:
                    return 0.0;
            };
        }

        public void setIO(bool e1, bool e2)
        {

            byte temp = (byte)(bool2byte(e1) << 1);
            temp |= (byte)(bool2byte(e2));
            s_senddata.io = temp;
        }

        public bool getIO(int ch)
        {

            switch (ch)
            {
                case 0:
                    if ((byte)(s_data.io & 0x01) == 0x01)
                        return true;
                    else
                        return false;
                case 1:
                    if ((byte)(s_data.io & 0x02) == 0x02)
                        return true;
                    else
                        return false;
                case 2:
                    if ((byte)(s_data.io & 0x04) == 0x04)
                        return true;
                    else
                        return false;
                case 3:
                    if ((byte)(s_data.io & 0x08) == 0x08)
                        return true;
                    else
                        return false;
                case 4:
                    if ((byte)(s_data.io & 0x10) == 0x10)
                        return true;
                    else
                        return false;
                default:
                    return false;
            };
           
        }

        public void setRelay(int ch, bool v)
        {
            s_senddata.relay = setBit(s_senddata.relay, (byte)ch, v);
        }

        public bool getRelay(int ch)
        {
            switch (ch)
            {
                case 0:
                    if ((s_data.relay & 0x01) == 0x01)
                        return true;
                    else
                        return false;
                case 1:
                    if ((s_data.relay & 0x02) == 0x02)
                        return true;
                    else
                        return false;
                case 2:
                    if ((s_data.relay & 0x04) == 0x04)
                        return true;
                    else
                        return false;
                case 3:
                    if ((s_data.relay & 0x08) == 0x08)
                        return true;
                    else
                        return false;
                case 4:
                    if ((s_data.relay & 0x10) == 0x10)
                        return true;
                    else
                        return false;
                case 5:
                    if ((s_data.relay & 0x20) == 0x20)
                        return true;
                    else
                        return false;
                case 6:
                    if ((s_data.relay & 0x40) == 0x40)
                        return true;
                    else
                        return false;
                case 7:
                    if ((s_data.relay & 0x80) == 0x80)
                        return true;
                    else
                        return false;
                default:
                    return false;
            };
        }

        public void setWakeUP(DateTime time)
        {
            wakeUpDate = time;
            sendWakeUPDate = true;
        }

        private static byte setBit(byte temp, byte bit, bool v)
        {
            byte ret;
            if (v)
                ret = (byte)(temp | (byte)(0x01 << bit));
            else
            {   
              
                ret = (byte)(temp & (byte)(~(0x01 << bit)));
            }
                
            return ret;
        }

        private static void sendData(Object o)
        {
            COMMessage msg = new COMMessage();

            if (sendWakeUPDate)
            {
                msg.id = PC_ID_SEND_WUDATE;
                msg.dlc = 6;
                msg.setData((byte)(wakeUpDate.Year - 2000), 0);
                msg.setData((byte)wakeUpDate.Month, 1);
                msg.setData((byte)wakeUpDate.Day, 2);
                msg.setData((byte)wakeUpDate.Hour, 3);
                msg.setData((byte)wakeUpDate.Minute, 4);
                msg.setData((byte)wakeUpDate.Second, 5);
                sendWakeUPDate = false;
            }
            else if (sendCurDate)
            {
                DateTime now = DateTime.Now;
                msg.id = PC_ID_SEND_DATE;
                msg.dlc = 6;
                msg.setData((byte)(now.Year - 2000), 0);
                msg.setData((byte)now.Month, 1);
                msg.setData((byte)now.Day, 2);
                msg.setData((byte)now.Hour, 3);
                msg.setData((byte)now.Minute, 4);
                msg.setData((byte)now.Second, 5);
                sendCurDate = false;
            }
            else
            {
                int size = Marshal.SizeOf(s_senddata);
                byte[] byteArray = new byte[size];
                IntPtr pointer = Marshal.AllocHGlobal(size);
                Marshal.StructureToPtr(s_senddata, pointer, false);
                Marshal.Copy(pointer, byteArray, 0, size);
                Marshal.FreeHGlobal(pointer);
                msg.id = PC_ID_SEND_DATA;
                msg.dlc = (byte)size;
                msg.data = byteArray;               
            }

            Console.WriteLine("next msg: id: " + msg.id + "  dlc: " + msg.dlc + "sec: " + DateTime.Now.Second + " ms: " + DateTime.Now.Millisecond);
            socket.sendCOMMsg(msg);
           
        }

        private void readDate(COMMessage rx)
        {
            byte y, m, d, h, min, s;
            y = rx.getData(0);
            m = rx.getData(1);
            d = rx.getData(2);
            h = rx.getData(3);
            min = rx.getData(4);
            s = rx.getData(5);            
            mc_date = new DateTime(y+2000, m, d, h, min, s);
        }

        private void readWUDate(COMMessage rx)
        {
            byte y, m, d, h, min, s;
            y = rx.getData(0);
            m = rx.getData(1);
            d = rx.getData(2);
            h = rx.getData(3);
            min = rx.getData(4);
            s = rx.getData(5);
            mc_wudate = new DateTime(y + 2000, m, d, h, min, s);
        }

        private void readData(COMMessage rx)
        {
            int test = Marshal.SizeOf(s_data);
            GCHandle pinnedPacket = GCHandle.Alloc(rx.data, GCHandleType.Pinned);
            s_data = (S_DATA)Marshal.PtrToStructure(pinnedPacket.AddrOfPinnedObject(), typeof(S_DATA));
            pinnedPacket.Free();
        }

        private static double tempByte2TempDouble(byte d)
        {
            byte t = (byte)(d >> 1);
            double temp = Convert.ToDouble(t);
            if ((byte)(d & 0xFE) != 0)
            {
                temp += 0.5;
            }
            return temp;
        }

        private static byte bool2byte(bool v)
        {
            if (v)
                return 1;
            else
                return 0;

        }

        private static byte string2byte(String str)
        {
            char[] temp = str.ToCharArray();
            byte ret = 0;

            if(temp.Length == 1)
                ret = (byte)(temp[0]-0x30);
            else if (temp.Length == 2)
            {
                ret = (byte)((temp[0]-0x30) * 10);
                ret += (byte)(temp[1]-0x30);
            }

            return ret;
        }

    }




}
